<!DOCTYPE HTML>
<html lang="zh-CN">

<head>
    <!--Setting-->
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
    <meta http-equiv="Cache-Control" content="no-siteapp">
    <meta http-equiv="Cache-Control" content="no-transform">
    <meta name="renderer" content="webkit|ie-comp|ie-stand">
    <meta name="apple-mobile-web-app-capable" content="涛哥的博客">
    <meta name="apple-mobile-web-app-status-bar-style" content="black">
    <meta name="format-detection" content="telephone=no,email=no,adress=no">
    <meta name="browsermode" content="application">
    <meta name="screen-orientation" content="portrait">
    <meta name="theme-version" content="1.2.3">
    <meta name="root" content="/">
    <link rel="dns-prefetch" href="https://jaredtao.github.io">
    <!--SEO-->

<meta name="keywords" content="Qt,Qml,QtQuick,特效" />


<meta name="description" content="
简介
关于文章
ShaderEffect
显示器如何显示色彩
GPU渲染流程
渲染管线图
并行管线示意图


着色器语言编码规范
着色器代码示例
示例
着色器代码
顶点着色器
片段着色器


..." />


<meta name="robots" content="all" />
<meta name="google" content="all" />
<meta name="googlebot" content="all" />
<meta name="verify" content="all" />
    <!--Title-->

<title>
    
    玩转Qml(15)-着色器效果ShaderEffect |
    
    涛哥的博客
</title>

<link rel="alternate" href="/atom.xml" title="涛哥的博客" type="application/atom+xml">


<link rel="icon" href="/favicon.ico">

    


<link rel="stylesheet" href="/css/bootstrap.min.css?rev=3.3.7.css">
<link rel="stylesheet" href="/css/font-awesome.min.css?rev=4.7.0.css">
<link rel="stylesheet" href="/css/style.css?rev=@@hash.css">

    
<div class="hide">
    <script type="text/javascript">
    var cnzz_protocol = (("https:" == document.location.protocol) ? " https://" : " http://");
    document.write(unescape("%3Cspan class='cnzz_stat_icon_1263868967 hide' %3E%3Cscript%20src%3D%22https%3A%2F%2Fs95.cnzz.com%2Fz_stat.php%3Fweb_id%3D1272564536%22%3E%3C%2Fscript%3E%3C/span%3E%3Cscript src='" + cnzz_protocol + "s19.cnzz.com/z_stat.php%3Fid%3D1263868967%26show%3Dpic1' type='text/javascript'%3E%3C/script%3E"));
    </script>
</div>




    

<script>
(function() {
    var bp = document.createElement('script');
    var curProtocol = window.location.protocol.split(':')[0];
    if (curProtocol === 'https') {
        bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';
    } else {
        bp.src = 'http://push.zhanzhang.baidu.com/push.js';
    }
    var s = document.getElementsByTagName("script")[0];
    s.parentNode.insertBefore(bp, s);
})();
</script>

<meta name="generator" content="Hexo 4.2.1"></head>
<!--[if lte IE 8]>
<style>
    html{ font-size: 1em }
</style>
<![endif]-->
<!--[if lte IE 9]>
<div style="ie">你使用的浏览器版本过低，为了你更好的阅读体验，请更新浏览器的版本或者使用其他现代浏览器，比如Chrome、Firefox、Safari等。</div>
<![endif]-->
<body>
    <header class="main-header"  style="background-image:url(
    http://snippet.shenliyang.com/img/banner.jpg)"
     >
    <div class="main-header-box">
        <a class="header-avatar" href="/" title='JaredTao'>
            <img src="/img/avatar.jpg" alt="logo头像" class="img-responsive center-block">
        </a>
        <div class="branding">
            <!--<h2 class="text-hide">Snippet主题,从未如此简单有趣</h2>-->
            
            <h2>
                单枪匹马亦对饮，历经磨难记初心
            </h2>
            
        </div>
    </div>
</header>
    <nav class="main-navigation">
    <div class="container">
        <div class="row">
            <div class="col-sm-12">
                <div class="navbar-header"><span class="nav-toggle-button collapsed pull-right" data-toggle="collapse" data-target="#main-menu" id="mnav">
                        <span class="sr-only"></span>
                        <i class="fa fa-bars"></i>
                    </span>
                    <a class="navbar-brand" href="https://jaredtao.github.io">
                        涛哥的博客</a>
                </div>
                <div class="collapse navbar-collapse" id="main-menu">
                    <ul class="menu">
                        
                        <li role="presentation" class="text-center">
                            <a href="/"><i class="fa "></i>
                                首页</a>
                        </li>
                        
                        <li role="presentation" class="text-center">
                            <a href="/categories/玩转Qt/"><i class="fa "></i>
                                玩转Qt</a>
                        </li>
                        
                        <li role="presentation" class="text-center">
                            <a href="/categories/玩转Qml/"><i class="fa "></i>
                                玩转Qml</a>
                        </li>
                        
                        <li role="presentation" class="text-center">
                            <a href="/categories/玩转QtQuick/"><i class="fa "></i>
                                玩转QtQuick</a>
                        </li>
                        
                        <li role="presentation" class="text-center">
                            <a href="/categories/涛哥的博客/"><i class="fa "></i>
                                涛哥的博客</a>
                        </li>
                        
                        <li role="presentation" class="text-center">
                            <a href="/archives/"><i class="fa "></i>
                                时间轴</a>
                        </li>
                        
                    </ul>
                </div>
            </div>
        </div>
    </div>
</nav>
    <section class="content-wrap">
        <div class="container">
            <div class="row">
                <main class="col-md-8 main-content m-post">
                    <p id="process"></p>
<article class="post">
    <div class="post-head">
        <h1 id="玩转Qml(15)-着色器效果ShaderEffect">
            
            玩转Qml(15)-着色器效果ShaderEffect
            
        </h1>
        <div class="post-meta">
    
    <span class="categories-meta fa-wrap">
        <i class="fa fa-folder-open-o"></i>
        <a class="category-link" href="/categories/%E7%8E%A9%E8%BD%ACQml/">玩转Qml</a>
    </span>
    
    
    <span class="fa-wrap">
        <i class="fa fa-tags"></i>
        <span class="tags-meta">
            
            <a class="tag-link" href="/tags/Qml/" rel="tag">Qml</a> <a class="tag-link" href="/tags/Qt/" rel="tag">Qt</a> <a class="tag-link" href="/tags/QtQuick/" rel="tag">QtQuick</a> <a class="tag-link" href="/tags/%E7%89%B9%E6%95%88/" rel="tag">特效</a>
            
        </span>
    </span>
    
    
    
    <span class="fa-wrap">
        <i class="fa fa-clock-o"></i>
        <span class="date-meta">
            2019/06/22</span>
    </span>
    
    <span class="fa-wrap">
        <i class="fa fa-eye"></i>
        <span id="busuanzi_value_page_pv"></span>
    </span>
    
    
</div>
        
        
        <p class="fa fa-exclamation-triangle warning">
            本文于<strong>
                1018</strong>
            天之前发表，文中内容可能已经过时。
        </p>
        
    </div>
    
<div id="post-gallery">
    
    <img src="/images/ShaderEffect/7.png" alt="gallery-img" class="gallery">
    
</div>

    <div class="post-body post-content">
        <ul>
<li><a href="#%E7%AE%80%E4%BB%8B">简介</a></li>
<li><a href="#%E5%85%B3%E4%BA%8E%E6%96%87%E7%AB%A0">关于文章</a></li>
<li><a href="#ShaderEffect">ShaderEffect</a></li>
<li><a href="#%E6%98%BE%E7%A4%BA%E5%99%A8%E5%A6%82%E4%BD%95%E6%98%BE%E7%A4%BA%E8%89%B2%E5%BD%A9">显示器如何显示色彩</a></li>
<li><a href="#GPU%E6%B8%B2%E6%9F%93%E6%B5%81%E7%A8%8B">GPU渲染流程</a><ul>
<li><a href="#%E6%B8%B2%E6%9F%93%E7%AE%A1%E7%BA%BF%E5%9B%BE">渲染管线图</a></li>
<li><a href="#%E5%B9%B6%E8%A1%8C%E7%AE%A1%E7%BA%BF%E7%A4%BA%E6%84%8F%E5%9B%BE">并行管线示意图</a></li>
</ul>
</li>
<li><a href="#%E7%9D%80%E8%89%B2%E5%99%A8%E8%AF%AD%E8%A8%80%E7%BC%96%E7%A0%81%E8%A7%84%E8%8C%83">着色器语言编码规范</a></li>
<li><a href="#%E7%9D%80%E8%89%B2%E5%99%A8%E4%BB%A3%E7%A0%81%E7%A4%BA%E4%BE%8B">着色器代码示例</a><ul>
<li><a href="#%E7%A4%BA%E4%BE%8B">示例</a></li>
<li><a href="#%E7%9D%80%E8%89%B2%E5%99%A8%E4%BB%A3%E7%A0%81">着色器代码</a></li>
<li><a href="#%E9%A1%B6%E7%82%B9%E7%9D%80%E8%89%B2%E5%99%A8">顶点着色器</a></li>
<li><a href="#%E7%89%87%E6%AE%B5%E7%9D%80%E8%89%B2%E5%99%A8">片段着色器</a></li>
</ul>
</li>
<li><a href="#%E5%8F%82%E8%80%83%E6%96%87%E7%8C%AE">参考文献</a></li>
</ul>
<h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><p>这次涛哥将会教大家一些ShaderEffect(参考QmlBook,译作：着色器效果)的相关知识。</p>
<p>前面的文章，给大家展示了进场动画，以及页面切换动画，大部分都使用了ShaderEffect，所以这次专门来说一下ShaderEffect。</p>
<h2 id="源码"><a href="#源码" class="headerlink" title="源码"></a>源码</h2><p>《玩转Qml》系列文章，配套了一个优秀的开源项目:TaoQuick</p>
<p>github <a href="https://github.com/jaredtao/TaoQuick" target="_blank" rel="noopener">https://github.com/jaredtao/TaoQuick</a></p>
<p>访问不了或者速度太慢，可以用国内的镜像网站gitee</p>
<p><a href="https://gitee.com/jaredtao/TaoQuick" target="_blank" rel="noopener">https://gitee.com/jaredtao/TaoQuick</a></p>
<h2 id="ShaderEffect"><a href="#ShaderEffect" class="headerlink" title="ShaderEffect"></a>ShaderEffect</h2><p>动画只能控制组件的属性整体的变化，做特效需要精确到像素。</p>
<p>Qml中提供了ShaderEffect这个组件，就能实现像素级别的操作。</p>
<p>ShaderEffect允许我们在Qml的渲染引擎SceneGraph上，利用强大的GPU进行渲染。</p>
<p>使用ShaderEffect，需要有一些图形学知识，了解GPU渲染管线，了解图形API如OpenGL、DirectX等，同时也需要一些数学知识。</p>
<p>图形学的知识体系还是非常庞大的，要系统的学习，需要看很多书籍。入门级的比如“红宝书”《OpenGL编程指南》、“蓝宝书”《OpenGL超级宝典》……</p>
<p>一篇文章是说不完的，涛哥水平也有限。所以本文从实用的角度出发，按照涛哥自己的理解，提炼一些必要的知识点，省略一些无关的细节，</p>
<p>让各位Qt开发者能了解GPU原理，能看懂、甚至于自己写一些简单的着色器代码，就大功告成了。说的不对的地方，也欢迎大佬来指点。</p>
<h2 id="显示器如何显示色彩"><a href="#显示器如何显示色彩" class="headerlink" title="显示器如何显示色彩"></a>显示器如何显示色彩</h2><p>先来了解一下，显示器是如何显示出各种色彩的。</p>
<p>假如我们把显示器的画面放大100倍，就会看到很多整齐排列的像素点。</p>
<p><img src="/images/ShaderEffect/1.png" alt=""></p>
<p>继续放大，就会发现每个像素点，由三种发光的元件组成，这三种元件分别发出红、绿、蓝三种颜色的光。三种颜色的光组合在一起，</p>
<p>就是人眼看到的颜色。这就是著名的RGB颜色模型。</p>
<p><img src="/images/ShaderEffect/2.png" alt=""></p>
<p>如果把这三种光的亮度分为255个等级，就能组合出16777216种不同颜色的光。</p>
<p>GPU的任务，就是通过计算，给出每一个像素的红、绿、蓝 （简称r g b）三种颜色的数值，让显示器去”发出相应的光”。</p>
<p>(这样说可能不太严谨、不太专业，只是方便大家理解。另一方面，本文的目的，</p>
<p>是让大家学习如何写特效，不是去造显卡/造显示器。所以请专业人士见谅！)</p>
<p>注：参考[1]</p>
<h2 id="GPU渲染流程"><a href="#GPU渲染流程" class="headerlink" title="GPU渲染流程"></a>GPU渲染流程</h2><p>我们以画一个填充色的三角形为例，来说明</p>
<p><img src="/images/ShaderEffect/3.png" alt=""></p>
<h3 id="渲染管线图"><a href="#渲染管线图" class="headerlink" title="渲染管线图"></a>渲染管线图</h3><p>下图是一个简易的渲染管线，引用自 <a href="https://learnopengl-cn.readthedocs.io/zh/latest/" target="_blank" rel="noopener">LearnOpenGL</a></p>
<p><img src="/images/ShaderEffect/5.png" alt=""></p>
<p>画一个三角形，要经历顶点着色器、图元装配、几何着色器、片段着色器、光栅化等阶段。</p>
<p>其中蓝色部分是可以自定义的，自定义是指，按照图形API规范，写一段GPU能编译、运行的代码。</p>
<p>(这种代码就是着色器代码。可以自定义的这种渲染管线，就是可编程渲染管线，与之相对的是古老的固定渲染管线。)</p>
<p>这里各个阶段，分别引用一下，LearnOpenGL中的介绍(看不懂可以先跳过，看我画的图)：</p>
<figure class="highlight angelscript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">1</span> 管线的第一个部分是顶点着色器(Vertex Shader)，它把一个单独的顶点作为输入。顶点着色器主要的目的是</span><br><span class="line"></span><br><span class="line">把<span class="number">3</span>D坐标转为另一种<span class="number">3</span>D坐标（后面会解释），同时顶点着色器允许我们对顶点属性进行一些基本处理。</span><br><span class="line"></span><br><span class="line"><span class="number">2</span> 图元装配(Primitive Assembly)阶段将顶点着色器输出的所有顶点作为输入（如果是GL_POINTS，那么就是一个顶点），</span><br><span class="line"></span><br><span class="line">并所有的点装配成指定图元的形状；本节例子中是一个三角形。</span><br><span class="line"></span><br><span class="line"><span class="number">3</span> 图元装配阶段的输出会传递给几何着色器(Geometry Shader)。几何着色器把图元形式的一系列顶点的集合作为输入，</span><br><span class="line"></span><br><span class="line">它可以通过产生新顶点构造出新的（或是其它的）图元来生成其他形状。例子中，它生成了另一个三角形。</span><br><span class="line"></span><br><span class="line"><span class="number">4</span> 几何着色器的输出会被传入光栅化阶段(Rasterization Stage)，这里它会把图元映射为最终屏幕上相应的像素，</span><br><span class="line"></span><br><span class="line">生成供片段着色器(Fragment Shader)使用的片段(Fragment)。在片段着色器运行之前会执行裁切(Clipping)。</span><br><span class="line"></span><br><span class="line">裁切会丢弃超出你的视图以外的所有像素，用来提升执行效率。</span><br><span class="line"></span><br><span class="line"><span class="number">5</span> 片段着色器的主要目的是计算一个像素的最终颜色，这也是所有OpenGL高级效果产生的地方。通常，片段着色器包</span><br><span class="line"></span><br><span class="line">含<span class="number">3</span>D场景的数据（比如光照、阴影、光的颜色等等），这些数据可以被用来计算最终像素的颜色。</span><br></pre></td></tr></table></figure>

<h3 id="并行管线示意图"><a href="#并行管线示意图" class="headerlink" title="并行管线示意图"></a>并行管线示意图</h3><p>概念还是挺多的，而且很多教程都有渲染管线图。但是涛哥觉得，对于我们开发Shader来说，一定要有并行的意识，然而大部分</p>
<p>管线图，都没有体现出GPU的并行特性。所以涛哥自己画了一个草图：</p>
<p><img src="/images/ShaderEffect/6.png" alt=""></p>
<p>解释一下吧，CPU传入了3个顶点到GPU，GPU将这三个顶点，传递给三个顶点着色器。</p>
<p>这里要意识到，顶点着色器开始，就是并行处理了。GPU是很强大的SIMD架构（单指令流多数据流）。</p>
<p>如果我们自定义了一段顶点着色器代码，则三个顶点会同时运行这段代码。（后面的片段着色器代码，就是N个点同时运行）</p>
<p>顶点着色器进行处理，传递给图元装配。</p>
<p>图元装配阶段，进行了顶点扩充，变成N个点，N看作三角形面积所在的点。</p>
<p>之后N个点依次传给 几何着色器-&gt;光栅化-&gt;片段着色器，最后经过测试与混合后，输出到屏幕。</p>
<p>可以自定义编程的，有顶点着色器、几何着色器、片段着色器（有的地方也叫像素着色器），顺带提一下，还有另外三种：</p>
<p>曲面控制着色器、曲面评估着色器 和 计算着色器。</p>
<p>一般我们的关注点，都会在片段着色器上。涛哥之前写的12种特效，就只用了自定义的片段着色器。</p>
<p>著名的ShaderToy网站，也是只关注片段着色器。<a href="https://www.shadertoy.com" target="_blank" rel="noopener">ShaderToy</a></p>
<h2 id="着色器语言编码规范"><a href="#着色器语言编码规范" class="headerlink" title="着色器语言编码规范"></a>着色器语言编码规范</h2><p>我们可以把着色器语言，当作运行在GPU上的C语言。</p>
<p>Qt的ShaderEffect支持的着色器语言包括OpenGL规范中的GLSL，和DirectX规范中的HLSL，这两种着色语法上有些细微的区别，但是可以互相转换。</p>
<p>我们就以glsl为主。详细的语言规范，在khronos的官网, 各个版本都有: <a href="https://www.khronos.org/registry/OpenGL/specs/gl/" target="_blank" rel="noopener">https://www.khronos.org/registry/OpenGL/specs/gl/</a></p>
<p><img src="/images/ShaderEffect/8.png" alt=""></p>
<p>桌面版 OpenGL 版本众多，而嵌入式系统也有专用的OpenGL ES。</p>
<p>安卓手机、平板设备一般就是OpenGL ES，新的设备都支持ES 3.0，老的设备一般只支持到ES 2.0</p>
<p>OpenGL ES 的语言规范文档在这里： <a href="https://www.khronos.org/registry/OpenGL/specs/es/2.0/" target="_blank" rel="noopener">https://www.khronos.org/registry/OpenGL/specs/es/2.0/</a></p>
<p>我们就用Qt默认的版本。</p>
<h2 id="着色器代码示例"><a href="#着色器代码示例" class="headerlink" title="着色器代码示例"></a>着色器代码示例</h2><h3 id="示例"><a href="#示例" class="headerlink" title="示例"></a>示例</h3><p>这里用Qt帮助文档中的示例代码，来说明。</p>
<figure class="highlight qml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> QtQuick <span class="number">2.0</span></span><br><span class="line"></span><br><span class="line"><span class="title">Rectangle</span> &#123;</span><br><span class="line">    <span class="attribute">width</span>: <span class="number">200</span>; <span class="attribute">height</span>: <span class="number">100</span></span><br><span class="line">    <span class="title">Row</span> &#123;</span><br><span class="line">        <span class="title">Image</span> &#123; <span class="attribute">id:</span><span class="string"> img</span>;</span><br><span class="line">                <span class="title">sourceSize</span> &#123; <span class="attribute">width</span>: <span class="number">100</span>; <span class="attribute">height</span>: <span class="number">100</span> &#125; <span class="attribute">source</span>: <span class="string">"qt-logo.png"</span> &#125;</span><br><span class="line">        <span class="title">ShaderEffect</span> &#123;</span><br><span class="line">            <span class="attribute">width</span>: <span class="number">100</span>; <span class="attribute">height</span>: <span class="number">100</span></span><br><span class="line">            <span class="keyword">property</span><span class="string"> variant src</span>: img</span><br><span class="line">            <span class="attribute">vertexShader</span>: <span class="string">"</span></span><br><span class="line"><span class="string">                uniform highp mat4 qt_Matrix;</span></span><br><span class="line"><span class="string">                attribute highp vec4 qt_Vertex;</span></span><br><span class="line"><span class="string">                attribute highp vec2 qt_MultiTexCoord0;</span></span><br><span class="line"><span class="string">                varying highp vec2 coord;</span></span><br><span class="line"><span class="string">                void main() &#123;</span></span><br><span class="line"><span class="string">                    coord = qt_MultiTexCoord0;</span></span><br><span class="line"><span class="string">                    gl_Position = qt_Matrix * qt_Vertex;</span></span><br><span class="line"><span class="string">                &#125;"</span></span><br><span class="line">            <span class="attribute">fragmentShader</span>: <span class="string">"</span></span><br><span class="line"><span class="string">                varying highp vec2 coord;</span></span><br><span class="line"><span class="string">                uniform sampler2D src;</span></span><br><span class="line"><span class="string">                uniform lowp float qt_Opacity;</span></span><br><span class="line"><span class="string">                void main() &#123;</span></span><br><span class="line"><span class="string">                    lowp vec4 tex = texture2D(src, coord);</span></span><br><span class="line"><span class="string">                    gl_FragColor = vec4(vec3(dot(tex.rgb,</span></span><br><span class="line"><span class="string">                                        vec3(0.344, 0.5, 0.156))),</span></span><br><span class="line"><span class="string">                                             tex.a) * qt_Opacity;</span></span><br><span class="line"><span class="string">                &#125;"</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>这段代码的效果是</p>
<p><img src="/images/ShaderEffect/7.png" alt=""></p>
<p>左边是本来的绿色的Qt的logo，右边是处理过后的灰色logo。</p>
<h3 id="着色器代码"><a href="#着色器代码" class="headerlink" title="着色器代码"></a>着色器代码</h3><p>ShaderEffect的vertexShader属性就是顶点着色器了，其内容是一段字符串。按照着色器规范实现的。</p>
<p>同样的，fragmentShader属性 即片段着色器。</p>
<p>我们能在着色器中看到void main函数，这个便是着色器代码的入口函数，和C语言很像。</p>
<p>在main之前，还有一些全局变量,我们逐条来说明一下</p>
<p>在顶点着色器中，有这三种不同用处的变量：uniform、attribute、varying。</p>
<p>这些变量的值都是从CPU传递过来的。</p>
<p>如果你写过原生OpenGL的代码，就会知道，其中很大一部分工作，就是在处理CPU数据传递到GPU着色器中。</p>
<p>而Qml的ShaderEffect简化了这些工作，只要写一个property，名字、类型和着色器中的对应上，就可以了。</p>
<h3 id="顶点着色器"><a href="#顶点着色器" class="headerlink" title="顶点着色器"></a>顶点着色器</h3><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">attribute</span> <span class="keyword">highp</span> <span class="type">vec4</span> qt_Vertex;</span><br></pre></td></tr></table></figure>
<p>attribute是”属性”变量，按照前面涛哥画的管线图来说，三个顶点着色器同时运行时，每个着色器中</p>
<p>的attribute值都不一样。这里的qt_Vertex，可以理解为分别是三角形的三个顶点。</p>
<p>highp是精度限定符，这里先忽略，具体细节可以参考语言规范文档。后面的lowp、 medium也是精度限定符。</p>
<p>vec4就是四维向量，类似QVector4D。</p>
<p>qt_Vertex是变量的名字。</p>
<p>这条语句的作用，就是声明一个用来存储顶点的attribute变量qt_Vertex。</p>
<p>uniform是统一变量，三个顶点着色器同时运行时，它们取得的uniform变量值是一样的。</p>
<p>varying表示这个顶点着色器的输出数据，将传递给后面的渲染管线。</p>
<figure class="highlight nginx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">void</span> main() </span><br><span class="line">&#123;</span><br><span class="line">    <span class="attribute">coord</span> = qt_MultiTexCoord0;</span><br><span class="line">    <span class="attribute">gl_Position</span> = qt_Matrix * qt_Vertex;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>这段main函数，将CPU传进来的纹理坐标qt_MultiTexCoord0数据，通过varying变量coord，传递给了下一个阶段，然后使用矩阵进行了坐标转换，</p>
<p>并将结果存储在glsl的内置变量gl_Position中。</p>
<h3 id="片段着色器"><a href="#片段着色器" class="headerlink" title="片段着色器"></a>片段着色器</h3><p>片段着色器中，就没有attribute了。uniform是一样的统一变量，varying是上一个阶段传递进来的数据。</p>
<figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">uniform</span> <span class="type">sampler2D</span> src;</span><br></pre></td></tr></table></figure>
<p>sampler2D是二维纹理。所谓纹理嘛，可以理解成一张图片，一个Image。</p>
<p>src这个变量，就代表外面传进来的那个Image。 sampler2D也可以是任意可视的Item(通过ShaderEffectSource传递进来)</p>
<p>来看一下main函数</p>
<figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> main() </span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">lowp</span> <span class="type">vec4</span> tex = <span class="built_in">texture2D</span>(src, coord);</span><br><span class="line">    <span class="built_in">gl_FragColor</span> = <span class="type">vec4</span>(<span class="type">vec3</span>(<span class="built_in">dot</span>(tex.rgb,<span class="type">vec3</span>(<span class="number">0.344</span>, <span class="number">0.5</span>, <span class="number">0.156</span>))), tex.a) * qt_Opacity;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>这里使用了纹理</p>
<figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">lowp</span> <span class="type">vec4</span> tex = <span class="built_in">texture2D</span>(src, coord);</span><br></pre></td></tr></table></figure>

<p>texture2D是一个内置函数，专业术语叫“对纹理进行采样”，什么意思呢？</p>
<p>假如coord的值是(0,0),那就是对src指代的这张图片，取x=0、y=0的坐标点的像素，作为返回值，存储在tex变量中。</p>
<p>这里注意一下纹理坐标的取值范围。假如Qml中图片的大小是100x100，其取值范围从(0, 0) -&gt; (100, 100)</p>
<p>这里的传进来的纹理坐标，取值范围是(0, 0) -&gt; (1, 1) ，GPU为了方便计算，都进行了归1化处理。将范围缩小到0 - 1</p>
<figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">gl_FragColor</span> = <span class="type">vec4</span>(<span class="type">vec3</span>(<span class="built_in">dot</span>(tex.rgb, <span class="type">vec3</span>(<span class="number">0.344</span>, <span class="number">0.5</span>, <span class="number">0.156</span>) )), tex.a) * qt_Opacity;</span><br></pre></td></tr></table></figure>
<p>dot(tex.rgb, vec3(0.344, 0.5, 0.156) ) 是对两个三维向量进行了点乘。</p>
<p>tex.rgb是GLSL中的取值器语法。 tex是一个四维变量，可以用tex.r tex.g tex.b tex.a分别取出其中一维，也可以任意两个组合、三个</p>
<p>组合取值。 </p>
<p>rgba可以取值，xyzw也可以取值， stpq也行，但只能三种选一种，不能混用。</p>
<p>vec4（vec3(), tex.a) 是用三维向量再加一个变量，构造四维向量。</p>
<p>这条语句其实是一个RGB转灰度的公式，可以自行搜索相关的资料。</p>
<p>gl_FragColor 是内置变量，表示所在片段着色器的最终的输出颜色。</p>
<h2 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h2><p>[1] <a href="https://zhuanlan.zhihu.com/p/43467096" target="_blank" rel="noopener">https://zhuanlan.zhihu.com/p/43467096</a></p>
<p>[2] <a href="https://learnopengl-cn.github.io/" target="_blank" rel="noopener">https://learnopengl-cn.github.io/</a></p>

    </div>
    
    <div class="reward" ontouchstart>
    <div class="reward-wrap">赏
        <div class="reward-box">
            
            <span class="reward-type">
                <img class="alipay" src="/img/zhifubao.jpg"><b>支付宝打赏</b>
            </span>
            
            
            <span class="reward-type">
                <img class="wechat" src="/img/weixin.jpg"><b>微信打赏</b>
            </span>
            
        </div>
    </div>
    <p class="reward-tip">
        为众人抱薪者,不可使其冻毙于霜雪
    </p>
</div>
    
    <div class="post-footer">
        <div>
            
            转载声明：
            文章采用<a href="http://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议</a>进行许可, 转载请注明出处 © <a href="https://jaredtao.github.io" target="_blank">武威的涛哥</a>
            
            
        </div>
        <div>
            
        </div>
    </div>
</article>
<div class="article-nav prev-next-wrap clearfix">
    
    <a href="/2019/07/04/%E7%8E%A9%E8%BD%ACQml(16)-%E7%A7%BB%E6%A4%8DShaderToy/" class="pre-post btn btn-default" title='玩转Qml(16)-移植ShaderToy'>
        <i class="fa fa-angle-left fa-fw"></i><span class="hidden-lg">上一篇</span>
        <span class="hidden-xs">
            玩转Qml(16)-移植ShaderToy</span>
    </a>
    
    
    <a href="/2019/06/09/%E7%8E%A9%E8%BD%ACQml(14)-%E5%8A%A8%E7%94%BB%E7%89%B9%E6%95%88-%E6%A2%AF%E5%BA%A6/" class="next-post btn btn-default" title='玩转Qml(14)-动画特效-梯度'>
        <span class="hidden-lg">下一篇</span>
        <span class="hidden-xs">
            玩转Qml(14)-动画特效-梯度</span><i class="fa fa-angle-right fa-fw"></i>
    </a>
    
</div>

<div id="comments">
    

<div id="vcomments" class="valine"></div>

<script src="//cdn1.lncld.net/static/js/3.0.4/av-min.js"></script>
<script src="/assets/valine.min.js"></script>

<script>
new Valine({
    av: AV,
    el: '#vcomments',
    appId: 'qegD4552u6FU9HoNDvRsonko-gzGzoHsz',
    appKey: 'eitB2FV5n2qyMHcDM7x6EQpt',
    placeholder: '说点什么吧',
    notify: false,
    verify: true,
    avatar: 'mm',
    meta: 'nick,mail'.split(','),
    pageSize: '10',
    path: window.location.pathname,
    lang: 'zh-CN'.toLowerCase()
})
</script>


</div>


                </main>
                
                    <aside id="article-toc" role="navigation" class="col-md-4">
    <div class="widget">
        <h3 class="title">
            文章目录
        </h3>
        
        <ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#简介"><span class="toc-text">简介</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#源码"><span class="toc-text">源码</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#ShaderEffect"><span class="toc-text">ShaderEffect</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#显示器如何显示色彩"><span class="toc-text">显示器如何显示色彩</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#GPU渲染流程"><span class="toc-text">GPU渲染流程</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#渲染管线图"><span class="toc-text">渲染管线图</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#并行管线示意图"><span class="toc-text">并行管线示意图</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#着色器语言编码规范"><span class="toc-text">着色器语言编码规范</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#着色器代码示例"><span class="toc-text">着色器代码示例</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#示例"><span class="toc-text">示例</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#着色器代码"><span class="toc-text">着色器代码</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#顶点着色器"><span class="toc-text">顶点着色器</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#片段着色器"><span class="toc-text">片段着色器</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#参考文献"><span class="toc-text">参考文献</span></a></li></ol>
        
    </div>
</aside>
                
            </div>
        </div>
    </section>
    <footer class="main-footer">
    <div class="container">
        <div class="row">
        </div>
    </div>
</footer>
<a id="back-to-top" class="icon-btn hide">
    <i class="fa fa-chevron-up"></i>
</a>
    <div class="copyright">
    <div class="container">
        <div class="row">
            <div class="col-sm-12">
                <div class="busuanzi">
    
    访问量:
    <strong id="busuanzi_value_site_pv">
        <i class="fa fa-spinner fa-spin"></i>
    </strong>
    &nbsp; | &nbsp;
    访客数:
    <strong id="busuanzi_value_site_uv">
        <i class="fa fa-spinner fa-spin"></i>
    </strong>
    
</div>
            </div>
            <div class="col-sm-12">
                <span>Copyright &copy;
                    2019
                    
                </span> |
                <span>
                    Powered by <a href="//hexo.io" class="copyright-links" target="_blank" rel="nofollow">Hexo</a>
                </span> |
                <span>
                    Theme by <a href="//github.com/shenliyang/hexo-theme-snippet.git" class="copyright-links" target="_blank" rel="nofollow">Snippet</a>
                </span>
            </div>
        </div>
    </div>
</div>



<script src="/assets/tagcanvas.min.js?rev=2.9.js"></script>

<script>
var tagOption = {
    textColour: '#444', // 字体颜色
    outlineMethod: 'block', // 选中模式
    outlineColour: '#FFDAB9', // 选中模式的颜色
    interval: 30 || 30, // 动画帧之间的时间间隔，值越大，转动幅度越大
    textHeight: 13,
    outlineRadius: 3,
    freezeActive: true || '', // 选中的标签是否继续滚动
    frontSelect: true || '', // 不选标签云后部的标签
    initial: [0.1, -0.1],
    depth: 0.5,
    decel: 0.95,
    maxSpeed: 0.03,
    reverse: true || '', // 是否反向触发
    fadeIn: 500, // 进入动画时间
    wheelZoom: false || '' // 是否启用鼠标滚轮
}
TagCanvas.Start('tag-cloud-3d', '', tagOption);
</script>


<script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>


<script src="/js/app.js?rev=@@hash.js"></script>

</body>
</html>